# coding:utf-8
from typing import Union

from PySide6.QtCore import Qt, QRectF, QSize, Property, QRect, QPoint
from PySide6.QtGui import QIcon, QPainter, QColor
from PySide6.QtWidgets import QPushButton, QApplication, QWidget, QToolButton

from qfluentwidgets import setFont, FluentIconBase, drawIcon, Theme
from qfluentwidgets.common.icon import toQIcon
from qfluentwidgets.common.overload import singledispatchmethod
from ...common import ElStyleSheet


class PushButton(QPushButton):
    """ Push button

    Constructors
    ------------
    * PushButton(`parent`: QWidget = None)
    * PushButton(`text`: str, `parent`: QWidget = None, `icon`: QIcon | str | FluentIconBase = None)
    * PushButton(`icon`: QIcon | FluentIcon, `text`: str, `parent`: QWidget = None)
    """

    @singledispatchmethod
    def __init__(self, parent: QWidget = None):
        super().__init__(parent)
        ElStyleSheet.BUTTON.apply(self)
        self.isPressed = False
        self.isHover = False

        self._icon = QIcon()
        self._text = ''
        self._normalColor = QColor()
        self._hoverColor = QColor()
        self._pressedColor = QColor()
        self._disabledColor = QColor()
        self._alignment = Qt.AlignmentFlag.AlignCenter

        self.setIconSize(QSize(16, 16))
        self.setIcon(None)
        setFont(self)
        self._postInit()

    @__init__.register
    def _(self, text: str, parent: QWidget = None, icon: Union[QIcon, str, FluentIconBase] = None):
        self.__init__(parent=parent)
        self.setText(text)
        self.setIcon(icon)

    @__init__.register
    def _(self, icon: QIcon, text: str, parent: QWidget = None):
        self.__init__(text, parent, icon)

    @__init__.register
    def _(self, icon: FluentIconBase, text: str, parent: QWidget = None):
        self.__init__(text, parent, icon)

    def _postInit(self):
        pass

    def setIcon(self, icon: Union[QIcon, str, FluentIconBase]):
        self.setProperty('hasIcon', icon is not None)
        self.setStyle(QApplication.style())
        self._icon = icon or QIcon()
        self.update()

    def icon(self):
        return toQIcon(self._icon)

    def setText(self, text: str):
        self._text = text
        self.update()

    def text(self) -> str:
        return self._text

    def setNormalColor(self, color: QColor):
        self._normalColor = color
        self.update()

    def getNormalColor(self) -> QColor:
        return self._normalColor

    def setHoverColor(self, color: QColor):
        self._hoverColor = color
        self.update()

    def getHoverColor(self) -> QColor:
        return self._hoverColor

    def setPressedColor(self, color: QColor):
        self._pressedColor = color
        self.update()

    def getPressedColor(self) -> QColor:
        return self._pressedColor

    def setDisabledColor(self, color: QColor):
        self._disabledColor = color
        self.update()

    def getDisabledColor(self) -> QColor:
        return self._disabledColor

    def setAlignment(self, alignment: Qt.AlignmentFlag):
        self._alignment = alignment

    def getAlignment(self) -> Qt.AlignmentFlag:
        return self._alignment

    def setProperty(self, name: str, value) -> bool:
        if name != 'icon':
            return super().setProperty(name, value)

        self.setIcon(value)
        return True

    def mousePressEvent(self, e):
        self.isPressed = True
        super().mousePressEvent(e)

    def mouseReleaseEvent(self, e):
        self.isPressed = False
        super().mouseReleaseEvent(e)

    def enterEvent(self, e):
        self.isHover = True
        self.update()

    def leaveEvent(self, e):
        self.isHover = False
        self.update()

    def _drawIcon(self, icon, painter, rect, state=QIcon.State.Off):
        """ draw icon """
        if isinstance(icon, FluentIconBase):
            if self.isPressed:
                icon = icon.icon(color=self.pressedColor)
            elif self.isHover:
                icon = icon.icon(color=self.hoverColor)
            elif not self.isEnabled():
                icon = icon.icon(color=self.disabledColor)
            else:
                icon = icon.icon(color=self.normalColor)
        drawIcon(icon, painter, rect, state)

    def paintEvent(self, e):
        super().paintEvent(e)

        painter = QPainter(self)
        painter.setRenderHints(
            QPainter.RenderHint.Antialiasing | QPainter.RenderHint.SmoothPixmapTransform | QPainter.RenderHint.TextAntialiasing)

        if not self.isEnabled():
            painter.setOpacity(0.3628)
        elif self.isPressed:
            painter.setOpacity(0.786)

        painter.setPen(self.normalColor)
        if self.isPressed:
            painter.setPen(self.pressedColor)
        elif self.isHover:
            painter.setPen(self.hoverColor)
        elif not self.isEnabled():
            painter.setPen(self.disabledColor)
        else:
            painter.setPen(self.normalColor)

        margin = 10
        spacing = 10
        if self.icon().isNull():
            painter.drawText(self.rect().adjusted(margin, 4, -margin, -4), self.alignment, self.text())
            return

        text = self.fontMetrics().elidedText(self.text(), Qt.TextElideMode.ElideRight,
                                             self.width() - margin * 2 - self.iconSize().width() - spacing)
        textWidth = self.fontMetrics().horizontalAdvance(text)
        if self.alignment == Qt.AlignmentFlag.AlignLeft:
            self._drawIcon(self._icon, painter,
                           QRect(QPoint(margin, int((self.height() - self.iconSize().height()) / 2)),
                                 self.iconSize()))
            rect = QRect(margin + self.iconSize().width() + spacing, 4,
                         self.width() - margin - self.iconSize().width() - spacing, self.height() - 8)
            painter.drawText(rect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, text)
        elif self.alignment == Qt.AlignmentFlag.AlignRight:
            width = self.iconSize().width() + margin * 2 + spacing + textWidth
            ix = self.width() - width + margin
            iconRect = QRect(QPoint(ix, (self.height() - self.iconSize().height()) // 2), self.iconSize())
            self._drawIcon(self._icon, painter, iconRect)
            tx = iconRect.right() + spacing
            textRect = QRect(QPoint(tx, 4), QSize(self.width() - tx, self.height() - 8))
            painter.drawText(textRect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, text)
        else:
            width = self.iconSize().width() + margin * 2 + spacing + textWidth
            ix = (self.width() - width) // 2
            if ix < margin:
                ix = margin

            iconRect = QRect(QPoint(ix, (self.height() - self.iconSize().height()) // 2), self.iconSize())
            self._drawIcon(self._icon, painter, iconRect)
            tx = iconRect.right() + spacing
            textRect = QRect(QPoint(tx, 4),
                             QSize(self.width() - iconRect.right() - margin - spacing, self.height() - 8))
            painter.drawText(textRect, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter, text)

    normalColor = Property(QColor, getNormalColor, setNormalColor)
    hoverColor = Property(QColor, getHoverColor, setHoverColor)
    pressedColor = Property(QColor, getPressedColor, setPressedColor)
    disabledColor = Property(QColor, getDisabledColor, setDisabledColor)
    alignment = Property(Qt.AlignmentFlag, getAlignment, setAlignment)


class PrimaryPushButton(PushButton):
    """ Primary push button

    Constructors
    ------------
    * PrimaryPushButton(`parent`: QWidget = None)
    * PrimaryPushButton(`text`: str, `parent`: QWidget = None, `icon`: QIcon | str | FluentIconBase = None)
    * PrimaryPushButton(`icon`: QIcon | FluentIcon, `text`: str, `parent`: QWidget = None)
    """

    def _drawIcon(self, icon, painter, rect, state=QIcon.State.Off):
        """ draw icon """
        if isinstance(icon, FluentIconBase):
            # icon = icon.icon(color='#ffffff')
            icon = icon.icon(Theme.DARK)
        drawIcon(icon, painter, rect, state)


class SuccessPrimaryPushButton(PrimaryPushButton):
    """ Success primary push button

    Constructors
    ------------
    * SuccessPrimaryPushButton(`parent`: QWidget = None)
    * SuccessPrimaryPushButton(`text`: str, `parent`: QWidget = None, `icon`: QIcon | str | FluentIconBase = None)
    * SuccessPrimaryPushButton(`icon`: QIcon | FluentIcon, `text`: str, `parent`: QWidget = None)
    """


class WarningPrimaryPushButton(PrimaryPushButton):
    """ Warning primary push button

    Constructors
    ------------
    * WarningPrimaryPushButton(`parent`: QWidget = None)
    * WarningPrimaryPushButton(`text`: str, `parent`: QWidget = None, `icon`: QIcon | str | FluentIconBase = None)
    * WarningPrimaryPushButton(`icon`: QIcon | FluentIcon, `text`: str, `parent`: QWidget = None)
    """


class DangerPrimaryPushButton(PrimaryPushButton):
    """ Danger primary push button

    Constructors
    ------------
    * DangerPrimaryPushButton(`parent`: QWidget = None)
    * DangerPrimaryPushButton(`text`: str, `parent`: QWidget = None, `icon`: QIcon | str | FluentIconBase = None)
    * DangerPrimaryPushButton(`icon`: QIcon | FluentIcon, `text`: str, `parent`: QWidget = None)
    """


class InfoPrimaryPushButton(PrimaryPushButton):
    """ Info primary push button

    Constructors
    ------------
    * InfoPrimaryPushButton(`parent`: QWidget = None)
    * InfoPrimaryPushButton(`text`: str, `parent`: QWidget = None, `icon`: QIcon | str | FluentIconBase = None)
    * InfoPrimaryPushButton(`icon`: QIcon | FluentIcon, `text`: str, `parent`: QWidget = None)
    """


class TransparentPushButton(PushButton):
    """ Transparent push button

    Constructors
    ------------
    * TransparentPushButton(`parent`: QWidget = None)
    * TransparentPushButton(`text`: str, `parent`: QWidget = None, `icon`: QIcon | str | FluentIconBase = None)
    * TransparentPushButton(`icon`: QIcon | FluentIcon, `text`: str, `parent`: QWidget = None)
    """


class ToolButton(QToolButton):
    """ Tool button

    Constructors
    ------------
    * ToolButton(`parent`: QWidget = None)
    * ToolButton(`icon`: QIcon | str | FluentIconBase, `parent`: QWidget = None)
    """

    @singledispatchmethod
    def __init__(self, parent: QWidget = None):
        super().__init__(parent)
        ElStyleSheet.BUTTON.apply(self)
        self.isPressed = False
        self.isHover = False

        self._normalColor = QColor()
        self._hoverColor = QColor()
        self._pressedColor = QColor()
        self._disabledColor = QColor()

        self.setIconSize(QSize(16, 16))
        self.setIcon(QIcon())
        setFont(self)
        self._postInit()

    @__init__.register
    def _(self, icon: FluentIconBase, parent: QWidget = None):
        self.__init__(parent)
        self.setIcon(icon)

    @__init__.register
    def _(self, icon: QIcon, parent: QWidget = None):
        self.__init__(parent)
        self.setIcon(icon)

    @__init__.register
    def _(self, icon: str, parent: QWidget = None):
        self.__init__(parent)
        self.setIcon(icon)

    def _postInit(self):
        pass

    def setIcon(self, icon: Union[QIcon, str, FluentIconBase]):
        self._icon = icon
        self.update()

    def icon(self):
        return toQIcon(self._icon)

    def setProperty(self, name: str, value) -> bool:
        if name != 'icon':
            return super().setProperty(name, value)

        self.setIcon(value)
        return True

    def mousePressEvent(self, e):
        self.isPressed = True
        super().mousePressEvent(e)

    def mouseReleaseEvent(self, e):
        self.isPressed = False
        super().mouseReleaseEvent(e)

    def enterEvent(self, e):
        self.isHover = True
        self.update()

    def leaveEvent(self, e):
        self.isHover = False
        self.update()

    def setNormalColor(self, color: QColor):
        self._normalColor = color
        self.update()

    def getNormalColor(self) -> QColor:
        return self._normalColor

    def setHoverColor(self, color: QColor):
        self._hoverColor = color
        self.update()

    def getHoverColor(self) -> QColor:
        return self._hoverColor

    def setPressedColor(self, color: QColor):
        self._pressedColor = color
        self.update()

    def getPressedColor(self) -> QColor:
        return self._pressedColor

    def setDisabledColor(self, color: QColor):
        self._disabledColor = color
        self.update()

    def getDisabledColor(self) -> QColor:
        return self._disabledColor

    def _drawIcon(self, icon, painter: QPainter, rect: QRectF, state=QIcon.State.Off):
        """ draw icon """
        if isinstance(icon, FluentIconBase):
            if self.isPressed:
                icon = icon.icon(color=self.pressedColor)
            elif self.isHover:
                icon = icon.icon(color=self.hoverColor)
            elif not self.isEnabled():
                icon = icon.icon(color=self.disabledColor)
            else:
                icon = icon.icon(color=self.normalColor)
        drawIcon(icon, painter, rect, state)

    def paintEvent(self, e):
        super().paintEvent(e)
        if self._icon is None:
            return

        painter = QPainter(self)
        painter.setRenderHints(QPainter.RenderHint.Antialiasing |
                               QPainter.RenderHint.SmoothPixmapTransform)

        if not self.isEnabled():
            painter.setOpacity(0.43)
        elif self.isPressed:
            painter.setOpacity(0.63)

        w, h = self.iconSize().width(), self.iconSize().height()
        y = (self.height() - h) / 2
        x = (self.width() - w) / 2
        self._drawIcon(self._icon, painter, QRectF(x, y, w, h))

    normalColor = Property(QColor, getNormalColor, setNormalColor)
    hoverColor = Property(QColor, getHoverColor, setHoverColor)
    pressedColor = Property(QColor, getPressedColor, setPressedColor)
    disabledColor = Property(QColor, getDisabledColor, setDisabledColor)


class PrimaryToolButton(ToolButton):
    """ Primary tool button
    Constructors
    ------------
    * PrimaryToolButton(`parent`: QWidget = None)
    * PrimaryToolButton(`icon`: QIcon | str | FluentIconBase, `parent`: QWidget = None)
    """


class SuccessPrimaryToolButton(PrimaryToolButton):
    """ Success primary tool button
    Constructors
    -
    * SuccessPrimaryToolButton(`parent`: QWidget = None)
    * SuccessPrimaryToolButton(`icon`: QIcon | str | FluentIconBase, `parent`: QWidget = None)
    """


class WarningPrimaryToolButton(PrimaryToolButton):
    """ Warning primary tool button
    Constructors
    -
    * WarningPrimaryToolButton(`parent`: QWidget = None)
    * WarningPrimaryToolButton(`icon`: QIcon | str | FluentIconBase, `parent`: QWidget = None)
    """


class DangerPrimaryToolButton(PrimaryToolButton):
    """ Danger primary tool button
    Constructors
    -
    * DangerPrimaryToolButton(`parent`: QWidget = None)
    * DangerPrimaryToolButton(`icon`: QIcon | str | FluentIconBase, `parent`: QWidget = None)
    """


class InfoPrimaryToolButton(PrimaryToolButton):
    """ Info primary tool button
    Constructors
    -
    * InfoPrimaryToolButton(`parent`: QWidget = None)
    * InfoPrimaryToolButton(`icon`: QIcon | str | FluentIconBase, `parent`: QWidget = None)
    """


class TransparentToolButton(ToolButton):
    """ Transparent tool button
    Constructors
    ------------
    * TransparentToolButton(`parent`: QWidget = None)
    * TransparentToolButton(`icon`: QIcon | str | FluentIconBase, `parent`: QWidget = None)
    """


ElButton = PushButton
ElPrimaryButton = PrimaryPushButton
ElSuccessPrimaryButton = SuccessPrimaryPushButton
ElWarningPrimaryButton = WarningPrimaryPushButton
ElDangerPrimaryButton = DangerPrimaryPushButton
ElInfoPrimaryButton = InfoPrimaryPushButton
ElTransparentButton = TransparentPushButton
ElToolButton = ToolButton
ElPrimaryToolButton = PrimaryToolButton
ElSuccessPrimaryToolButton = SuccessPrimaryToolButton
ElWarningPrimaryToolButton = WarningPrimaryToolButton
ElDangerPrimaryToolButton = DangerPrimaryToolButton
ElInfoPrimaryToolButton = InfoPrimaryToolButton
ElTransparentToolButton = TransparentToolButton
